/*
 * cache.c
 *
 *  Created on: 27/11/2011
 *      Author: utn_so
 */

#include "cache.h"
#include "iniciarFat.h"
#include "archivos_abiertos.h"

struct nodoDeCache * agregarALaCache(struct nodoDeCache * nodoParaAgregar, struct colaDeCache * cacheParaAgregar ) {

	/*
	 * Agrega un bloque a la cache. Si la cache tiene espacio disponible, retorna NULL
	 * Si es necesario sustituir un bloque, retorna el bloque para que sea enviado al disco
	 * Se agrega utilizando el algoritmo LRU para la sustitución de los bloques
	 */

	if (config.iTamCache == 0) //Si la cache está deshabilitada, retorno NULL
	{
		return NULL;
	}

	struct nodoDeCache * siguiente = cacheParaAgregar->nodoInicial;
	struct nodoDeCache * anterior;
	struct nodoDeCache *nodoAux;

	//Este while es para ver si está en la cola ya



	if(siguiente==NULL){
		cacheParaAgregar->nodoInicial = nodoParaAgregar;
		cacheParaAgregar->nodoFinal = nodoParaAgregar;
		cacheParaAgregar->cantidadDeNodosEnLaCola = 1;
		cacheParaAgregar->nodoInicial->siguiente = NULL;

		return NULL;
	}

	if (cacheParaAgregar->nodoInicial->numeroBloque == nodoParaAgregar->numeroBloque)
	{//si el numero de bloque del primer nodo es igual al numero de bloque a agregar
		struct nodoDeCache *nodoAux;
		nodoAux = cacheParaAgregar->nodoInicial;
		nodoParaAgregar->siguiente = cacheParaAgregar->nodoInicial->siguiente;
		cacheParaAgregar->nodoInicial = nodoParaAgregar;
		if (nodoAux == cacheParaAgregar->nodoFinal)
			cacheParaAgregar->nodoFinal == nodoParaAgregar;
		free (nodoAux->contenido);
		free (nodoAux);
		return NULL; //return nodoAux; No necesito enviarlo a disco
	//	cacheParaAgregar->cantidadDeNodosEnLaCola++; //no agrego tengo que cambiar el nodo
	}
	else
	{
		do {
			if (siguiente->numeroBloque == nodoParaAgregar->numeroBloque) {

				//El nodo ya está en la caché, hay que moverlo a la primer posición
				anterior->siguiente = siguiente->siguiente;
				nodoParaAgregar->siguiente = cacheParaAgregar->nodoInicial;
				cacheParaAgregar->nodoInicial = nodoParaAgregar;
				free(siguiente->contenido);
				free(siguiente);


				if (anterior->siguiente == NULL)
					cacheParaAgregar->nodoFinal = anterior;

				return NULL;
				//	free(siguiente);
				//	free(anterior);



			} else {
				anterior = siguiente;
				siguiente = siguiente->siguiente;
			}
		} while (siguiente != NULL);
	}
	//Ok, no está en la cola, así que hay que agregarlo

	char noEstaEnCache = 0;
	if (siguiente == NULL)
		noEstaEnCache = 1;

	nodoAux = NULL;

	if (cacheParaAgregar->cantidadDeNodosEnLaCola == cacheParaAgregar->cantidadDeNodosPosibles)
	{
		//Tengo que sacar un nodo.
		siguiente = cacheParaAgregar->nodoInicial;
		do {
			siguiente = siguiente->siguiente;
		} while (siguiente->siguiente != cacheParaAgregar->nodoFinal);



		nodoAux = cacheParaAgregar->nodoFinal;
		cacheParaAgregar->nodoFinal = siguiente;
		cacheParaAgregar->nodoFinal->siguiente = NULL;
		cacheParaAgregar->cantidadDeNodosEnLaCola--;
	}

	if (noEstaEnCache == 1)
	{
		nodoParaAgregar->siguiente = cacheParaAgregar->nodoInicial;
		cacheParaAgregar->nodoInicial = nodoParaAgregar;
		cacheParaAgregar->cantidadDeNodosEnLaCola++;
	}

	return nodoAux;

}



struct nodoDeCache * buscarEnCache(uint32_t numeroBloque, struct colaDeCache * cacheParaBuscar) {
	/*
	 * Busca el numero de Bloque en la cache.
	 * Retorna el bloque o NULL si no lo encuentra
	 */
	if (config.iTamCache == 0)
		return NULL;

	struct nodoDeCache *siguiente;
	struct nodoDeCache *anterior;

	//Este while es para ver si está en la cola ya

	if (cacheParaBuscar->nodoInicial == NULL)
	{
		log_error(log, "CACHE", "Nodo en la caché no encontrado");
		return NULL;
	}

	siguiente = cacheParaBuscar->nodoInicial;

	if (cacheParaBuscar->nodoInicial == cacheParaBuscar->nodoFinal)
		if (siguiente->numeroBloque == numeroBloque)
		{
			log_info(log, "CACHE", "Se encontró nodo en la caché, hay que subirlo de posición");
			return siguiente;
		}

	do {
		if (siguiente->numeroBloque == numeroBloque) {

			//El nodo ya está en la caché, hay que moverlo a la primer posición fixme

//			free(siguiente);
			log_info(log, "CACHE", "Se encontró nodo en la caché, hay que subirlo de posición");
			return siguiente;


		} else {
			anterior = siguiente;
			siguiente = siguiente->siguiente;
		}
	} while (siguiente != NULL);

	log_error(log, "CACHE", "Nodo en la caché no encontrado");
//	free(siguiente);
//	free(anterior);

	return NULL;
}

struct colaDeCache * iniciarCache() {
/*
 * inicializa la cache.
 */
	if (config.iTamCache == 0)
		return NULL;

	log_info(log, "CACHE", "Creación de la Cache");

	struct colaDeCache * cache = (struct colaDeCache *) malloc(sizeof(struct colaDeCache));
	cache->nodoFinal = NULL;
	cache->nodoInicial = NULL;
	cache->cantidadDeNodosEnLaCola = 0;
	cache->cantidadDeNodosPosibles = config.iTamCache;

	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutex_init(&(cache->mutexCacheArchivo) ,&attr);
	pthread_mutexattr_destroy(&attr);


//	sem_init(&cache->mutexCache, 0, 1);

return cache;

}

/*
 * Pasa la cache al disco y la borra
 */

uint32_t borrarCache(const char *path){
/*
 * Elimina la cache de un archivo. Mientras la elimina, actualiza los bloques en el disco
 */
	if (config.iTamCache == 0)
		return 0;

	struct colaDeCache * cacheParaEliminar = buscarCacheDeUnArchivo(path);

	struct nodoDeCache * siguiente = cacheParaEliminar->nodoInicial;
	struct nodoDeCache * paraEliminar;

	if (cacheParaEliminar->nodoInicial == NULL)
		return 0; //estaba vacía

	while(siguiente != NULL)
	{
		paraEliminar = siguiente;
		siguiente = siguiente->siguiente;
		enviarBloque(paraEliminar->contenido, paraEliminar->numeroBloque, 0);
		free(paraEliminar->contenido);
		free(paraEliminar);
	}

	cacheParaEliminar->nodoInicial = NULL;
	cacheParaEliminar->nodoFinal = NULL;

//	free (cacheParaEliminar->nodoFinal);
//	free(cacheParaEliminar->nodoInicial);  // ya se borró
//	free(cacheParaEliminar);  //lo hacemos en el release

return 0;


}

